<template>
  <section id="app" class="todoapp">
    <header class="header">
      <h1>todos</h1>
      <input
        class="new-todo"
        placeholder="What needs to be done?"
        autocomplete="off"
        autofocus
        v-model="input"
        @keyup.enter="addTodo"
        >
    </header>
    <section class="main" v-show="count">
      <input id="toggle-all" class="toggle-all" v-model="allDone" type="checkbox">
      <label for="toggle-all">Mark all as complete</label>
      <ul class="todo-list">
        <!-- 遍历绑定数组 设置样式以隐藏文本框 -->
        <!-- key绑定todi.text时 文本框输入会重新渲染导致输入框失焦 -->
        <li
          v-for="todo in filteredTodos"
          :key="todo"
          :class="{ editing: todo === editingTodo, completed: todo.completed }"
        >
          <div class="view">
            <input class="toggle" type="checkbox" v-model="todo.completed">
            <!-- 双击文本显示输入框并初始化 -->
            <label @dblclick="editTodo(todo)">{{ todo.text }}</label>
            <button class="destroy" @click="remove(todo)"></button>
          </div>
          <!-- 回车、失焦及esc事件触发 -->
          <input
            class="edit"
            type="text"
            v-editing-focus="todo === editingTodo"
            v-model="todo.text"
            @keyup.enter="doneEdit(todo)"
            @blur="doneEdit(todo)"
            @keyup.esc="cancelEdit(todo)"
            >
        </li>
      </ul>
    </section>
    <footer class="footer" v-show="count">
      <span class="todo-count">
        <strong>{{ remainingCount }}</strong> {{ remainingCount > 1 ? 'items' : 'item' }} left
      </span>
      <ul class="filters">
        <li><a href="#/all">All</a></li>
        <li><a href="#/active">Active</a></li>
        <li><a href="#/completed">Completed</a></li>
      </ul>
      <button class="clear-completed" @click="removeCompleted" v-show="count > remainingCount">
        Clear completed
      </button>
    </footer>
  </section>
  <footer class="info">
    <p>Double-click to edit a todo</p>
    <!-- Remove the below line ↓ -->
    <p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
    <!-- Change this out with your name and url ↓ -->
    <p>Created by <a href="https://www.lagou.com">教瘦</a></p>
    <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
  </footer>
</template>

<script>
import './assets/index.css'
import useLocalStorage from './utils/useLocalStorage'
import { ref, computed, onMounted, onUnmounted, watchEffect } from 'vue'

const storage = useLocalStorage()

// 1. 添加待办事项
/**
 * @param {Array} todos - 待办事项源数组
 */
const useAdd = todos => {
  // 定义文本框绑定数据 双向绑定为响应式数据
  const input = ref('')
  // 文本框回车触发事件
  const addTodo = () => {
    // 使用value获取文本值，若有值则去除前后文本框
    const text = input.value && input.value.trim()
    // 无值返回
    if (text.length === 0) return
    // 有值从数组头部添加，注意todos为响应式数据，因此使用value
    todos.value.unshift({
      text,
      completed: false
    })
    // 清空文本框
    input.value = ''
  }
  // 返回成员
  return {
    input,
    addTodo
  }
}

// 2. 删除待办事项
/**
 * @param {Array} todos - 待办事项源数组
 */
const useRemove = todos => {
  /**
   * 真正删除的函数
   * @params todo - 删除项
   */
  const remove = todo => {
    // 源数据中找到该项并删除
    // 注意取数据时需要 .value
    const index = todos.value.indexOf(todo)
    todos.value.splice(index, 1)
  }

  const removeCompleted = () => {
    todos.value = todos.value.filter(todo => !todo.completed)
  }
  // 将函数放出去
  return {
    remove,
    removeCompleted
  }
}

// 3. 编辑待办项
/**
 * @param {Function} remove - 删除函数
 */
const useEdit = remove => {
  // 编辑前文本
  let beforeEditingText = ''
  // 当前编辑状态 有值为编辑中 无值为编辑完成
  // 用于控制文本框显示及聚焦
  const editingTodo = ref(null)

  /**
   * 编辑函数
   * todo - 当前待办事项
   */
  const editTodo = todo => {
    // 记录初始文本数据
    beforeEditingText = todo.text
    // 记录当前待办事项状态
    editingTodo.value = todo
  }

  /**
   * 回车或失焦完成编辑
   * todo - 当前待办事项
   */
  const doneEdit = todo => {
    // 状态无值则直接返回
    if (!editingTodo.value) return
    // 去掉前后空格
    todo.text = todo.text.trim()
    // 新输入的文本框无值则删除
    todo.text || remove(todo)
    // 将待办事项重置为null
    editingTodo.value = null
  }

  /**
   * 取消编辑
   * todo - 当前待办事项
   */
  const cancelEdit = todo => {
    // 将待办事项重置为null
    editingTodo.value = null
    // 将文本值还原为初始值
    todo.text = beforeEditingText
  }
  return {
    editingTodo,
    editTodo,
    doneEdit,
    cancelEdit
  }
}

// 4. 切换待办项完成状态
const useFilter = todos => {
  const allDone = computed({
    get () {
      return !todos.value.filter(todo => !todo.completed).length
    },
    set (value) {
      todos.value.forEach(todo => {
        todo.completed = value
      })
    }
  })

  const filter = {
    all: list => list,
    active: list => list.filter(todo => !todo.completed),
    completed: list => list.filter(todo => todo.completed)
  }
  const type = ref('all')
  const filteredTodos = computed(() => filter[type.value](todos.value))
  const remainingCount = computed(() => filter.active(todos.value).length)
  const count = computed(() => todos.value.length)

  const onHashChange = () => {
    const hash = window.location.hash.replace('#/', '')
    if (filter[hash]) {
      type.value = hash
    } else {
      type.value = 'all'
      window.location.hash = ''
    }
  }

  onMounted(() => {
    window.addEventListener('hashchange', onHashChange)
    onHashChange()
  })

  onUnmounted(() => {
    window.removeEventListener('hashchange', onHashChange)
  })

  return {
    allDone,
    count,
    filteredTodos,
    remainingCount
  }
}

// 5. 存储待办事项
const useStorage = () => {
  const KEY = 'TODOKEYS'
  const todos = ref(storage.getItem(KEY) || [])
  watchEffect(() => {
    storage.setItem(KEY, todos.value)
  })
  return todos
}

export default {
  name: 'App',
  // 将components更改为setup,即composition API入口
  setup () {
    // 创建待办事项数组
    const todos = useStorage()

    const { remove, removeCompleted } = useRemove(todos)

    return {
      // 注意返回使用值
      todos,
      remove,
      removeCompleted,
      // 调用函数并获取返回值
      ...useAdd(todos),
      ...useEdit(remove),
      ...useFilter(todos)
    }
  },
  directives: {
    editingFocus: (el, binding) => {
      binding.value && el.focus()
    }
  }
}
</script>

<style>
</style>
